﻿#include "PassFlow.h"
#include "Mesh.h"
#include "worklet/ComputeResidual.h"
#include "worklet/Context.h"
#include "worklet/InitialCondition.h"



struct PassFlow::Context : EquationContext
{
  Context(const Configuration& cfg)
    : EquationContext(cfg)
  {
  }

  template<typename CoordType>
  VTKM_EXEC_CONT ValueType InitialCondition(const CoordType& p)
  {

    ValueType primary_value = ValueType{ 1, 1, 0, 0, 1 / _gamma / _mach / _mach };
    return PrimaryToConservation(primary_value);
  }

  template<typename CoordType>
  VTKM_EXEC_CONT ValueType
  BoundaryCondition(const BCType& bc_type__, const ValueType& ul, const Vec3f& normal, const CoordType& p)
  {
    ValueType ur;
    BCEnum bc_type = static_cast<BCEnum>(bc_type__);
    if (bc_type == BCEnum::Outlet) // 出口
    {
      ur[0] = ul[0];
      ur[1] = ul[1];
      ur[2] = ul[2];
      ur[3] = ul[3];
      ur[4] = ul[4];
    }
    else if (bc_type == BCEnum::Inlet) // 进口
    {
      ValueType primary_value;
      primary_value = ValueType{ 1, 1, 0, 0, 1 / _gamma / _mach / _mach };

      ValueType value;
      auto r = primary_value[0];
      auto u = primary_value[1];
      auto v = primary_value[2];
      auto w = primary_value[3];
      auto pre = primary_value[4];

      ur[0] = r;
      ur[1] = r * u;
      ur[2] = r * v;
      ur[3] = r * w;
      ur[4] = pre / _gamm1 + 0.5 * r * (u * u + v * v);
    }
    else if (bc_type == BCEnum::Wall) //wall
    {
      Vec3f mom{ ul[1], ul[2], ul[3] };
      Real vn = vtkm::Dot(mom, normal);
      ur[0] = ul[0];
      ur[1] = ul[1] - 2 * vn * normal[0];
      ur[2] = ul[2] - 2 * vn * normal[1];
      ur[3] = ul[3] - 2 * vn * normal[2];
      ur[4] = ul[4];
    }
    else if (bc_type == BCEnum::FarField) //wall
    {
      ValueType primary_value;
      primary_value = ValueType{ 1, 1, 0, 0, 1 / _gamma / _mach / _mach };

      ValueType value;
      auto r = primary_value[0];
      auto u = primary_value[1];
      auto v = primary_value[2];
      auto w = primary_value[3];
      auto pre = primary_value[4];

      ur[0] = r;
      ur[1] = r * u;
      ur[2] = r * v;
      ur[3] = r * w;
      ur[4] = pre / _gamm1 + 0.5 * r * (u * u + v * v);
      // TODO: Raise Error.
      //this->RaiseError("不可用的BC Type.");
    }
    else
    {
      console::Error("未知的BC type", int(bc_type));
    }

    return ur;
  }


  Context PrepareForExecution(vtkm::cont::DeviceAdapterId, vtkm::cont::Token&) const
  {
    return *this;
  }
};

PassFlow::PassFlow(const Configuration& cfg)
  : EulerEquation(cfg)
  , _context(new Context(cfg))
{
}


void PassFlow::InitialCondition()
{
  vtkm::cont::Invoker{}(Loops::WorkletInitCondition{},
                        _mesh.GetCellSet(),
                        _solution,
                        _mesh.GetCoordinate().GetDataAsMultiplexer(),
                        *_context);
}

void PassFlow::UpdateResidual()
{
  auto& transient = static_cast<Transient&>(*(_app.GetExecutioner()));
  Real current_time = transient.CurrentTime();

  _timer.Start();
  vtkm::cont::Invoker{}(Loops::FiniteVolumeWorklet{ current_time },
                        _mesh.GetCellSet(),
                        _mesh.GetCoordinate(),
                        _mesh.GetCellLinks(),
                        this->_solution,
                        this->_rhs,
                        *this->_context);

  console::Info("PassFlow::有限体积，", "时间: ", _timer.GetElapsedTime(), " s");
}
